package org.mat.ipaas.ucenter.service.modules.system.controller;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.jeecgframework.poi.excel.ExcelImportCheckUtil;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.mat.framework.lang.dto.MatApiResponse;
import org.mat.ipaas.ucenter.biz.common.config.TenantContext;
import org.mat.ipaas.ucenter.biz.common.constant.CacheConstant;
import org.mat.ipaas.ucenter.biz.common.constant.CommonConstant;
import org.mat.ipaas.ucenter.biz.common.constant.SymbolConstant;
import org.mat.ipaas.ucenter.biz.common.system.query.QueryGenerator;
import org.mat.ipaas.ucenter.biz.common.system.vo.DictModel;
import org.mat.ipaas.ucenter.biz.common.system.vo.DictQuery;
import org.mat.ipaas.ucenter.biz.common.util.ImportExcelUtil;
import org.mat.ipaas.ucenter.biz.common.util.RedisUtil;
import org.mat.ipaas.ucenter.biz.common.util.SqlInjectionUtil;
import org.mat.ipaas.ucenter.biz.common.util.oConvertUtils;
import org.mat.ipaas.ucenter.biz.config.mybatis.MybatisPlusSaasConfig;
import org.mat.ipaas.ucenter.biz.modules.system.entity.SysDict;
import org.mat.ipaas.ucenter.biz.modules.system.entity.SysDictItem;
import org.mat.ipaas.ucenter.biz.modules.system.model.SysDictTree;
import org.mat.ipaas.ucenter.biz.modules.system.model.TreeSelectModel;
import org.mat.ipaas.ucenter.biz.modules.system.security.DictQueryBlackListHandler;
import org.mat.ipaas.ucenter.biz.modules.system.service.ISysDictItemService;
import org.mat.ipaas.ucenter.biz.modules.system.service.ISysDictService;
import org.mat.ipaas.ucenter.biz.modules.system.vo.SysDictPage;
import org.mat.ipaas.ucenter.biz.modules.system.vo.lowapp.SysDictVo;
import org.mat.ipaas.ucenter.service.api.system.SysDictRestAPI;
import org.mat.ipaas.ucenter.service.dto.system.entity.SysDictDTO;
import org.mat.ipaas.ucenter.service.dto.system.entity.SysDictItemDTO;
import org.mat.ipaas.ucenter.service.dto.system.model.SysDictTreeDTO;
import org.mat.ipaas.ucenter.service.dto.system.model.TreeSelectModelDTO;
import org.mat.ipaas.ucenter.service.dto.system.vo.DictModelDTO;
import org.mat.ipaas.ucenter.service.dto.system.vo.DictQueryDTO;
import org.mat.ipaas.ucenter.service.dto.system.vo.LoginUserDTO;
import org.mat.ipaas.ucenter.service.dto.system.vo.lowapp.SysDictVoDTO;
import org.mat.ipaas.ucenter.service.modules.SessionService;
import org.mat.ipaas.ucenter.service.util.SessionUtils;
import org.mat.ipaas.ucenter.service.modules.TokenUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
 * <p>
 * 字典表 前端控制器
 * </p>
 *
 * @Author zhangweijian
 * @since 2018-12-28
 */
@RestController
@RequestMapping("/sys/dict")
@Slf4j
public class SysDictController implements SysDictRestAPI {

    @Autowired
    private SessionService sessionService;

    @Autowired
    private ISysDictService sysDictService;
    @Autowired
    private ISysDictItemService sysDictItemService;
    @Autowired
    public RedisTemplate redisTemplate;
    @Autowired
    private DictQueryBlackListHandler dictQueryBlackListHandler;
    @Autowired
    private RedisUtil redisUtil;

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public MatApiResponse<Page<SysDictDTO>> queryPageList(@SpringQueryMap SysDictDTO sysDictDTO,
                                                           @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                                           @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        MatApiResponse<Page<SysDictDTO>> matApiResponse = new MatApiResponse<>();
        SysDict sysDict = toSysDict(sysDictDTO);


        //------------------------------------------------------------------------------------------------
        //是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
        if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
            sysDict.setTenantId(oConvertUtils.getInt(TenantContext.getTenant(), 0));
        }
        //------------------------------------------------------------------------------------------------
        QueryWrapper<SysDict> queryWrapper = QueryGenerator.initQueryWrapper(sysDict, req.getParameterMap());
        Page<SysDict> page = new Page<SysDict>(pageNo, pageSize);
        IPage<SysDict> pageList = sysDictService.page(page, queryWrapper);
        log.debug("查询当前页：" + pageList.getCurrent());
        log.debug("查询当前页数量：" + pageList.getSize());
        log.debug("查询结果数量：" + pageList.getRecords().size());
        log.debug("数据总数：" + pageList.getTotal());
        matApiResponse.setSuccess(true);
        matApiResponse.setData(toSysDictDTOPage(pageList));
        return matApiResponse;
    }


    /**
     * @param sysDictDTO
     * @param pageNo
     * @param pageSize
     * @return
     * @功能：获取树形字典数据
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/treeList", method = RequestMethod.GET)
    public MatApiResponse<List<SysDictTreeDTO>> treeList(@SpringQueryMap SysDictDTO sysDictDTO,
                                                         @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                                         @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();

        MatApiResponse<List<SysDictTreeDTO>> matApiResponse = new MatApiResponse<>();
        SysDict sysDict = toSysDict(sysDictDTO);

        LambdaQueryWrapper<SysDict> query = new LambdaQueryWrapper<>();
        // 构造查询条件
        String dictName = sysDict.getDictName();
        if (oConvertUtils.isNotEmpty(dictName)) {
            query.like(true, SysDict::getDictName, dictName);
        }
        query.orderByDesc(true, SysDict::getCreateTime);
        List<SysDict> list = sysDictService.list(query);
        List<SysDictTree> treeList = new ArrayList<>();
        for (SysDict node : list) {
            treeList.add(new SysDictTree(node));
        }
        matApiResponse.setSuccess(true);
        matApiResponse.setData(toSysDictTreeDTOList(treeList));
        return matApiResponse;
    }


    /**
     * 获取全部字典数据
     *
     * @return
     */
    @RequestMapping(value = "/queryAllDictItems", method = RequestMethod.GET)
    public MatApiResponse<?> queryAllDictItems(HttpServletRequest request) {
        Map<String, List<DictModel>> res = new HashMap(5);
        res = sysDictService.queryAllDictItems();
        return MatApiResponse.success(res);
    }

    /**
     * 获取字典数据
     *
     * @param dictCode
     * @return
     */
    @RequestMapping(value = "/getDictText/{dictCode}/{key}", method = RequestMethod.GET)
    public MatApiResponse<String> getDictText(@PathVariable("dictCode") String dictCode, @PathVariable("key") String key) {
        log.info(" dictCode : " + dictCode);
        MatApiResponse<String> matApiResponse = new MatApiResponse<String>();
        String text = null;
        try {
            text = sysDictService.queryDictTextByKey(dictCode, key);
            matApiResponse.setSuccess(true);
            matApiResponse.setData(text);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            matApiResponse.fail("操作失败");
            return matApiResponse;
        }
        return matApiResponse;
    }


    /**
     * 获取字典数据 【接口签名验证】
     *
     * @param dictCode 字典code
     * @param dictCode 表名,文本字段,code字段  | 举例：sys_user,realname,id
     * @return
     */
    @RequestMapping(value = "/getDictItems/{dictCode}", method = RequestMethod.GET)
    public MatApiResponse<List<DictModelDTO>> getDictItems(@PathVariable("dictCode") String dictCode, @RequestParam(value = "sign", required = false) String sign, HttpServletRequest request) {
        log.info(" dictCode : " + dictCode);
        MatApiResponse<List<DictModelDTO>> matApiResponse = new MatApiResponse<>();

        //update-begin-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        if (!dictQueryBlackListHandler.isPass(dictCode)) {
            return matApiResponse.fail(dictQueryBlackListHandler.getError());
        }
        //update-end-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        try {
            List<DictModel> ls = sysDictService.getDictItems(dictCode);
            if (ls == null) {
                matApiResponse.fail("字典Code格式不正确！");
                return matApiResponse;
            }
            matApiResponse.setSuccess(true);
            matApiResponse.setData(toDictModelDTOList(ls));
            log.debug(matApiResponse.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            matApiResponse.fail("操作失败");
            return matApiResponse;
        }
        return matApiResponse;
    }


    /**
     * 【接口签名验证】
     * 【JSearchSelectTag下拉搜索组件专用接口】
     * 大数据量的字典表 走异步加载  即前端输入内容过滤数据
     *
     * @param dictCode 字典code格式：table,text,code
     * @return
     */
    @RequestMapping(value = "/loadDict/{dictCode}", method = RequestMethod.GET)
    public MatApiResponse<List<DictModelDTO>> loadDict(@PathVariable("dictCode") String dictCode,
                                                       @RequestParam(name = "keyword", required = false) String keyword,
                                                       @RequestParam(value = "sign", required = false) String sign,
                                                       @RequestParam(value = "pageSize", required = false) Integer pageSize) {

        //update-begin-author:taoyan date:2023-5-22 for: /issues/4905 因为中括号(%5)的问题导致的 表单生成器字段配置时，选择关联字段，在进行高级配置时，无法加载数据库列表，提示 Sgin签名校验错误！ #4905 RouteToRequestUrlFilter
        if (keyword != null && keyword.indexOf("%5") >= 0) {
            try {
                keyword = URLDecoder.decode(keyword, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error("下拉搜索关键字解码失败", e);
            }
        }
        //update-end-author:taoyan date:2023-5-22 for: /issues/4905 因为中括号(%5)的问题导致的  表单生成器字段配置时，选择关联字段，在进行高级配置时，无法加载数据库列表，提示 Sgin签名校验错误！ #4905

        log.info(" 加载字典表数据,加载关键字: " + keyword);
        MatApiResponse<List<DictModelDTO>> matApiResponse = new MatApiResponse<>();
        //update-begin-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        if (!dictQueryBlackListHandler.isPass(dictCode)) {
            return matApiResponse.fail(dictQueryBlackListHandler.getError());
        }
        //update-end-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        try {
            List<DictModel> ls = sysDictService.loadDict(dictCode, keyword, pageSize);
            if (ls == null) {
                matApiResponse.fail("字典Code格式不正确！");
                return matApiResponse;
            }
            matApiResponse.setSuccess(true);
            matApiResponse.setData(toDictModelDTOList(ls));
            log.info(matApiResponse.toString());
            return matApiResponse;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            matApiResponse.fail("操作失败");
            return matApiResponse;
        }
    }

    /**
     * 【接口签名验证】
     * 【给表单设计器的表字典使用】下拉搜索模式，有值时动态拼接数据
     *
     * @param dictCode
     * @param keyword  当前控件的值，可以逗号分割
     * @param sign
     * @param pageSize
     * @return
     */
    @RequestMapping(value = "/loadDictOrderByValue/{dictCode}", method = RequestMethod.GET)
    public MatApiResponse<List<DictModelDTO>> loadDictOrderByValue(
            @PathVariable("dictCode") String dictCode,
            @RequestParam(name = "keyword") String keyword,
            @RequestParam(value = "sign", required = false) String sign,
            @RequestParam(value = "pageSize", required = false) Integer pageSize) {
        // 首次查询查出来用户选中的值，并且不分页
        MatApiResponse<List<DictModelDTO>> firstRes = this.loadDict(dictCode, keyword, sign, null);
        if (!firstRes.getSuccess()) {
            return firstRes;
        }
        // 然后再查询出第一页的数据
        MatApiResponse<List<DictModelDTO>> matApiResponse = this.loadDict(dictCode, "", sign, pageSize);
        if (!matApiResponse.getSuccess()) {
            return matApiResponse;
        }
        // 合并两次查询的数据
        List<DictModelDTO> firstList = firstRes.getData();
        List<DictModelDTO> list = matApiResponse.getData();
        for (DictModelDTO firstItem : firstList) {
            // anyMatch 表示：判断的条件里，任意一个元素匹配成功，返回true
            // allMatch 表示：判断条件里的元素，所有的都匹配成功，返回true
            // noneMatch 跟 allMatch 相反，表示：判断条件里的元素，所有的都匹配失败，返回true
            boolean none = list.stream().noneMatch(item -> item.getValue().equals(firstItem.getValue()));
            // 当元素不存在时，再添加到集合里
            if (none) {
                list.add(0, firstItem);
            }
        }
        return matApiResponse;
    }

    /**
     * 【接口签名验证】
     * 根据字典code加载字典text 返回
     *
     * @param dictCode    顺序：tableName,text,code
     * @param keys        要查询的key
     * @param sign
     * @param delNotExist 是否移除不存在的项，默认为true，设为false如果某个key不存在数据库中，则直接返回key本身
     * @param request
     * @return
     */
    @RequestMapping(value = "/loadDictItem/{dictCode}", method = RequestMethod.GET)
    public MatApiResponse<List<String>> loadDictItem(@PathVariable("dictCode") String dictCode, @RequestParam(name = "key") String keys, @RequestParam(value = "sign", required = false) String sign, @RequestParam(value = "delNotExist", required = false, defaultValue = "true") boolean delNotExist, HttpServletRequest request) {
        MatApiResponse<List<String>> matApiResponse = new MatApiResponse<>();
        //update-begin-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        if (!dictQueryBlackListHandler.isPass(dictCode)) {
            return matApiResponse.fail(dictQueryBlackListHandler.getError());
        }
        //update-end-author:taoyan date:20220317 for: VUEN-222【安全机制】字典接口、online报表、online图表等接口，加一些安全机制
        try {
            if (dictCode.indexOf(SymbolConstant.COMMA) != -1) {
                String[] params = dictCode.split(SymbolConstant.COMMA);
                if (params.length != 3) {
                    matApiResponse.fail("字典Code格式不正确！");
                    return matApiResponse;
                }
                List<String> texts = sysDictService.queryTableDictByKeys(params[0], params[1], params[2], keys, delNotExist);

                matApiResponse.setSuccess(true);
                matApiResponse.setData(texts);
                log.info(matApiResponse.toString());
            } else {
                matApiResponse.fail("字典Code格式不正确！");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            matApiResponse.fail("操作失败");
            return matApiResponse;
        }

        return matApiResponse;
    }

    /**
     * 【接口签名验证】
     * 根据表名——显示字段-存储字段 pid 加载树形数据
     *
     * @param hasChildField   是否叶子节点字段
     * @param converIsLeafVal 是否需要系统转换 是否叶子节点的值 (0标识不转换、1标准系统自动转换)
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/loadTreeData", method = RequestMethod.GET)
    public MatApiResponse<List<TreeSelectModelDTO>> loadTreeData(@RequestParam(name = "pid", required = false) String pid, @RequestParam(name = "pidField") String pidField,
                                                                 @RequestParam(name = "tableName") String tbname,
                                                                 @RequestParam(name = "text") String text,
                                                                 @RequestParam(name = "code") String code,
                                                                 @RequestParam(name = "hasChildField") String hasChildField,
                                                                 @RequestParam(name = "converIsLeafVal", defaultValue = "1") int converIsLeafVal,
                                                                 @RequestParam(name = "condition") String condition,
                                                                 @RequestParam(value = "sign", required = false) String sign, HttpServletRequest request) {
        MatApiResponse<List<TreeSelectModelDTO>> matApiResponse = new MatApiResponse<>();
        Map<String, String> query = null;
        if (oConvertUtils.isNotEmpty(condition)) {
            query = JSON.parseObject(condition, Map.class);
        }
        // SQL注入漏洞 sign签名校验(表名,label字段,val字段,条件)
        String dictCode = tbname + "," + text + "," + code + "," + condition;
        SqlInjectionUtil.filterContent(dictCode);
        List<TreeSelectModel> ls = sysDictService.queryTreeList(query, tbname, text, code, pidField, pid, hasChildField, converIsLeafVal);
        matApiResponse.setSuccess(true);
        matApiResponse.setData(toTreeSelectModelDTOList(ls));
        return matApiResponse;
    }


    /**
     * 【APP接口】根据字典配置查询表字典数据（目前暂未找到调用的地方）
     *
     * @param dictQueryDTO
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Deprecated
    @GetMapping("/queryTableData")
    public MatApiResponse<List<DictModelDTO>> queryTableData(DictQueryDTO dictQueryDTO,
                                                             @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                                             @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                             @RequestParam(value = "sign", required = false) String sign, HttpServletRequest request) {
        MatApiResponse<List<DictModelDTO>> res = new MatApiResponse<>();
        DictQuery query = tioDictQuery(dictQueryDTO);

        // SQL注入漏洞 sign签名校验
        String dictCode = query.getTable() + "," + query.getText() + "," + query.getCode();
        SqlInjectionUtil.filterContent(dictCode);
        //update-begin-author:taoyan date:2022-11-4 for: issues/4128 sql injection
        if (!dictQueryBlackListHandler.isPass(dictCode)) {
            return res.fail(dictQueryBlackListHandler.getError());
        }
        //update-end-author:taoyan date:2022-11-4 for: issues/4128 sql injection
        List<DictModel> ls = this.sysDictService.queryDictTablePageList(query, pageSize, pageNo);
        res.setData(toDictModelDTOList(ls));
        res.setSuccess(true);
        return res;
    }

    /**
     * @param sysDictDTO
     * @return
     * @功能：新增
     */
    //@RequiresPermissions("system:dict:add")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public MatApiResponse<SysDictDTO> add(@RequestBody SysDictDTO sysDictDTO) {
        MatApiResponse<SysDictDTO> matApiResponse = new MatApiResponse<>();
        SysDict sysDict = toSysDict(sysDictDTO);


        try {
            sysDict.setCreateTime(new Date());
            sysDict.setDelFlag(CommonConstant.DEL_FLAG_0);
            sysDictService.save(sysDict);
            matApiResponse.success("保存成功！");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            matApiResponse.fail("操作失败");
        }
        return matApiResponse;
    }

    /**
     * @param sysDictDTO
     * @return
     * @功能：编辑
     */
    //@RequiresPermissions("system:dict:edit")
    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
    public MatApiResponse<SysDictDTO> edit(@RequestBody SysDictDTO sysDictDTO) {
        MatApiResponse<SysDictDTO> matApiResponse = new MatApiResponse<>();

        SysDict sysDict = toSysDict(sysDictDTO);
        SysDict sysdict = sysDictService.getById(sysDict.getId());
        if (sysdict == null) {
            matApiResponse.fail("未找到对应实体");
        } else {
            sysDict.setUpdateTime(new Date());
            boolean ok = sysDictService.updateById(sysDict);
            if (ok) {
                matApiResponse.success("编辑成功!");
            }
        }
        return matApiResponse;
    }

    /**
     * @param id
     * @return
     * @功能：删除
     */
    //@RequiresPermissions("system:dict:delete")
    @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
    @CacheEvict(value = {CacheConstant.SYS_DICT_CACHE, CacheConstant.SYS_ENABLE_DICT_CACHE}, allEntries = true)
    public MatApiResponse<SysDictDTO> delete(@RequestParam(name = "id", required = true) String id) {
        MatApiResponse<SysDictDTO> matApiResponse = new MatApiResponse<>();
        boolean ok = sysDictService.removeById(id);
        if (ok) {
            matApiResponse.success("删除成功!");
        } else {
            matApiResponse.fail("删除失败!");
        }
        return matApiResponse;
    }

    /**
     * @param ids
     * @return
     * @功能：批量删除
     */
    //@RequiresPermissions("system:dict:deleteBatch")
    @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
    @CacheEvict(value = {CacheConstant.SYS_DICT_CACHE, CacheConstant.SYS_ENABLE_DICT_CACHE}, allEntries = true)
    public MatApiResponse<SysDictDTO> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        MatApiResponse<SysDictDTO> matApiResponse = new MatApiResponse<>();
        if (oConvertUtils.isEmpty(ids)) {
            matApiResponse.fail("参数不识别！");
        } else {
            sysDictService.removeByIds(Arrays.asList(ids.split(",")));
            matApiResponse.success("删除成功!");
        }
        return matApiResponse;
    }

    /**
     * @return
     * @功能：刷新缓存
     */
    @RequestMapping(value = "/refleshCache")
    public MatApiResponse<?> refleshCache() {
        MatApiResponse<?> matApiResponse = new MatApiResponse<SysDict>();
        //清空字典缓存
//		Set keys = redisTemplate.keys(CacheConstant.SYS_DICT_CACHE + "*");
//		Set keys7 = redisTemplate.keys(CacheConstant.SYS_ENABLE_DICT_CACHE + "*");
//		Set keys2 = redisTemplate.keys(CacheConstant.SYS_DICT_TABLE_CACHE + "*");
//		Set keys21 = redisTemplate.keys(CacheConstant.SYS_DICT_TABLE_BY_KEYS_CACHE + "*");
//		Set keys3 = redisTemplate.keys(CacheConstant.SYS_DEPARTS_CACHE + "*");
//		Set keys4 = redisTemplate.keys(CacheConstant.SYS_DEPART_IDS_CACHE + "*");
//		Set keys5 = redisTemplate.keys( "jmreport:cache:dict*");
//		Set keys6 = redisTemplate.keys( "jmreport:cache:dictTable*");
//		redisTemplate.delete(keys);
//		redisTemplate.delete(keys2);
//		redisTemplate.delete(keys21);
//		redisTemplate.delete(keys3);
//		redisTemplate.delete(keys4);
//		redisTemplate.delete(keys5);
//		redisTemplate.delete(keys6);
//		redisTemplate.delete(keys7);

        //update-begin-author:liusq date:20230404 for:  [issue/4358]springCache中的清除缓存的操作使用了“keys”
        redisUtil.removeAll(CacheConstant.SYS_DICT_CACHE);
        redisUtil.removeAll(CacheConstant.SYS_ENABLE_DICT_CACHE);
        redisUtil.removeAll(CacheConstant.SYS_DICT_TABLE_CACHE);
        redisUtil.removeAll(CacheConstant.SYS_DICT_TABLE_BY_KEYS_CACHE);
        redisUtil.removeAll(CacheConstant.SYS_DEPARTS_CACHE);
        redisUtil.removeAll(CacheConstant.SYS_DEPART_IDS_CACHE);
        redisUtil.removeAll("jmreport:cache:dict");
        redisUtil.removeAll("jmreport:cache:dictTable");
        //update-end-author:liusq date:20230404 for:  [issue/4358]springCache中的清除缓存的操作使用了“keys”
        return matApiResponse;
    }

    /**
     * 导出excel
     *
     * @param request
     */
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(SysDictDTO sysDictDTO, HttpServletRequest request) {
        SysDict sysDict = toSysDict(sysDictDTO);

        //------------------------------------------------------------------------------------------------
        //是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
        if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
            sysDict.setTenantId(oConvertUtils.getInt(TenantContext.getTenant(), 0));
        }
        //------------------------------------------------------------------------------------------------

        // Step.1 组装查询条件
        QueryWrapper<SysDict> queryWrapper = QueryGenerator.initQueryWrapper(sysDict, request.getParameterMap());
        //Step.2 AutoPoi 导出Excel
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        List<SysDictPage> pageList = new ArrayList<SysDictPage>();

        List<SysDict> sysDictList = sysDictService.list(queryWrapper);
        for (SysDict dictMain : sysDictList) {
            SysDictPage vo = new SysDictPage();
            BeanUtils.copyProperties(dictMain, vo);
            // 查询机票
            List<SysDictItem> sysDictItemList = sysDictItemService.selectItemsByMainId(dictMain.getId());
            vo.setSysDictItemList(sysDictItemList);
            pageList.add(vo);
        }

        // 导出文件名称
        mv.addObject(NormalExcelConstants.FILE_NAME, "数据字典");
        // 注解对象Class
        mv.addObject(NormalExcelConstants.CLASS, SysDictPage.class);
        // 自定义表格参数
        LoginUserDTO user = sessionService.getLoginUser();

        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("数据字典列表", "导出人:" + user.getRealname(), "数据字典"));
        // 导出数据列表
        mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
        return mv;
    }

    /**
     * 通过excel导入数据
     *
     * @param
     * @return
     */
    //@RequiresPermissions("system:dict:importExcel")
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public MatApiResponse<?> importExcel() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            // 获取上传文件对象
            MultipartFile file = entity.getValue();
            ImportParams params = new ImportParams();
            params.setTitleRows(2);
            params.setHeadRows(2);
            params.setNeedSave(true);
            try {
                //导入Excel格式校验，看匹配的字段文本概率
                Boolean t = ExcelImportCheckUtil.check(file.getInputStream(), SysDictPage.class, params);
                if (t != null && !t) {
                    throw new RuntimeException("导入Excel校验失败 ！");
                }
                List<SysDictPage> list = ExcelImportUtil.importExcel(file.getInputStream(), SysDictPage.class, params);
                // 错误信息
                List<String> errorMessage = new ArrayList<>();
                int successLines = 0, errorLines = 0;
                for (int i = 0; i < list.size(); i++) {
                    SysDict po = new SysDict();
                    BeanUtils.copyProperties(list.get(i), po);
                    po.setDelFlag(CommonConstant.DEL_FLAG_0);
                    try {
                        Integer integer = sysDictService.saveMain(po, list.get(i).getSysDictItemList());
                        if (integer > 0) {
                            successLines++;
                            //update-begin---author:wangshuai ---date:20220211  for：[JTC-1168]如果字典项值为空，则字典项忽略导入------------
                        } else if (integer == -1) {
                            errorLines++;
                            errorMessage.add("字典名称：" + po.getDictName() + "，对应字典列表的字典项值不能为空，忽略导入。");
                        } else {
                            //update-end---author:wangshuai ---date:20220211  for：[JTC-1168]如果字典项值为空，则字典项忽略导入------------
                            errorLines++;
                            int lineNumber = i + 1;
                            //update-begin---author:wangshuai ---date:20220209  for：[JTC-1168]字典编号不能为空------------
                            if (oConvertUtils.isEmpty(po.getDictCode())) {
                                errorMessage.add("第 " + lineNumber + " 行：字典编码不能为空，忽略导入。");
                            } else {
                                errorMessage.add("第 " + lineNumber + " 行：字典编码已经存在，忽略导入。");
                            }
                            //update-end---author:wangshuai ---date:20220209  for：[JTC-1168]字典编号不能为空------------
                        }
                    } catch (Exception e) {
                        errorLines++;
                        int lineNumber = i + 1;
                        errorMessage.add("第 " + lineNumber + " 行：字典编码已经存在，忽略导入。");
                    }
                }
                return ImportExcelUtil.imporReturnRes(errorLines, successLines, errorMessage);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return MatApiResponse.fail("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    file.getInputStream().close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return MatApiResponse.fail("文件导入失败！");
    }


    /**
     * 查询被删除的列表
     *
     * @return
     */
    @RequestMapping(value = "/deleteList", method = RequestMethod.GET)
    public MatApiResponse<List<SysDictDTO>> deleteList() {
        MatApiResponse<List<SysDictDTO>> matApiResponse = new MatApiResponse<>();
        List<SysDict> list = this.sysDictService.queryDeleteList();
        matApiResponse.setSuccess(true);
        matApiResponse.setData(toSysDictDTOList(list));
        return matApiResponse;
    }


    /**
     * 物理删除
     *
     * @param id
     * @return
     */
    @RequestMapping(value = "/deletePhysic/{id}", method = RequestMethod.DELETE)
    public MatApiResponse<?> deletePhysic(@PathVariable("id") String id) {
        try {
            sysDictService.deleteOneDictPhysically(id);
            return MatApiResponse.success("删除成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return MatApiResponse.fail("删除失败!");
        }
    }

    /**
     * 逻辑删除的字段，进行取回
     *
     * @param id
     * @return
     */
    @RequestMapping(value = "/back/{id}", method = RequestMethod.PUT)
    public MatApiResponse<?> back(@PathVariable("id") String id) {
        try {
            sysDictService.updateDictDelFlag(0, id);
            return MatApiResponse.success("操作成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return MatApiResponse.fail("操作失败!");
        }
    }

    /**
     * VUEN-2584【issue】平台sql注入漏洞几个问题
     * 部分特殊函数 可以将查询结果混夹在错误信息中，导致数据库的信息暴露
     *
     * @param e
     * @return
     */
    @ExceptionHandler(java.sql.SQLException.class)
    public MatApiResponse<?> handleSQLException(Exception e) {
        String msg = e.getMessage();
        String extractvalue = "extractvalue";
        String updatexml = "updatexml";
        if (msg != null && (msg.toLowerCase().indexOf(extractvalue) >= 0 || msg.toLowerCase().indexOf(updatexml) >= 0)) {
            return MatApiResponse.fail("校验失败，sql解析异常！");
        }
        return MatApiResponse.fail("校验失败，sql解析异常！" + msg);
    }

    /**
     * 根据应用id获取字典列表和详情
     *
     * @param request
     */
    @GetMapping("/getDictListByLowAppId")
    public MatApiResponse<List<SysDictVoDTO>> getDictListByLowAppId(HttpServletRequest request) {
        String lowAppId = oConvertUtils.getString(TokenUtils.getLowAppIdByRequest(request), "0");
        List<SysDictVo> list = sysDictService.getDictListByLowAppId(lowAppId);
        return MatApiResponse.success(toSysDictVoDTOList(list));
    }


    /**
     * 添加字典
     *
     * @param sysDictVoDTO
     * @param request
     * @return
     */
    @PostMapping("/addDictByLowAppId")
    public MatApiResponse<String> addDictByLowAppId(@RequestBody SysDictVoDTO sysDictVoDTO, HttpServletRequest request) {
        SysDictVo sysDictVo = toSysDictVo(sysDictVoDTO);

        String lowAppId = oConvertUtils.getString(TokenUtils.getLowAppIdByRequest(request), "0");
        sysDictVo.setLowAppId(lowAppId);
        sysDictService.addDictByLowAppId(sysDictVo);
        return MatApiResponse.success("添加成功");
    }

    @PutMapping("/editDictByLowAppId")
    public MatApiResponse<String> editDictByLowAppId(@RequestBody SysDictVoDTO sysDictVoDTO, HttpServletRequest request) {
        SysDictVo sysDictVo = toSysDictVo(sysDictVoDTO);

        String lowAppId = oConvertUtils.getString(TokenUtils.getLowAppIdByRequest(request), "0");
        sysDictVo.setLowAppId(lowAppId);
        sysDictService.editDictByLowAppId(sysDictVo);
        return MatApiResponse.success("编辑成功");
    }


    private Page<SysDictDTO> toSysDictDTOPage(IPage<SysDict> pageList) {
        Page<SysDictDTO> sysDictDTOPage = new Page<>();
        List<SysDictDTO> sysDictDTOList = new ArrayList<>();
        if (pageList != null && CollectionUtils.isNotEmpty(pageList.getRecords())) {
            for (SysDict sysDict : pageList.getRecords()) {
                sysDictDTOList.add(toSysDictDTO(sysDict));
            }
        }

        sysDictDTOPage.setTotal(pageList.getTotal());
        sysDictDTOPage.setSize(pageList.getSize());
        sysDictDTOPage.setCurrent(pageList.getCurrent());
        sysDictDTOPage.setRecords(sysDictDTOList);
        return sysDictDTOPage;
    }

    private SysDictDTO toSysDictDTO(SysDict sysDict) {
        if (sysDict == null) {
            return null;
        }
        SysDictDTO sysDictDTO = new SysDictDTO();
        sysDictDTO.setId(sysDict.getId());
        sysDictDTO.setType(sysDict.getType());
        sysDictDTO.setDictName(sysDict.getDictName());
        sysDictDTO.setDictCode(sysDict.getDictCode());
        sysDictDTO.setDescription(sysDict.getDescription());
        sysDictDTO.setDelFlag(sysDict.getDelFlag());
        sysDictDTO.setCreateBy(sysDict.getCreateBy());
        sysDictDTO.setCreateTime(sysDict.getCreateTime());
        sysDictDTO.setUpdateBy(sysDict.getUpdateBy());
        sysDictDTO.setUpdateTime(sysDict.getUpdateTime());
        sysDictDTO.setTenantId(sysDict.getTenantId());
        sysDictDTO.setLowAppId(sysDict.getLowAppId());
        return sysDictDTO;
    }

    private SysDict toSysDict(SysDictDTO sysDictDTO) {
        if (sysDictDTO == null) {
            return null;
        }
        SysDict sysDict = new SysDict();
        sysDict.setId(sysDictDTO.getId());
        sysDict.setType(sysDictDTO.getType());
        sysDict.setDictName(sysDictDTO.getDictName());
        sysDict.setDictCode(sysDictDTO.getDictCode());
        sysDict.setDescription(sysDictDTO.getDescription());
        sysDict.setDelFlag(sysDictDTO.getDelFlag());
        sysDict.setCreateBy(sysDictDTO.getCreateBy());
        sysDict.setCreateTime(sysDictDTO.getCreateTime());
        sysDict.setUpdateBy(sysDictDTO.getUpdateBy());
        sysDict.setUpdateTime(sysDictDTO.getUpdateTime());
        sysDict.setTenantId(sysDictDTO.getTenantId());
        sysDict.setLowAppId(sysDictDTO.getLowAppId());
        return sysDict;
    }

    private List<SysDictTreeDTO> toSysDictTreeDTOList(List<SysDictTree> treeList) {
        List<SysDictTreeDTO> sysDictTreeDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(treeList)) {
            for (SysDictTree sysDictTree : treeList) {
                sysDictTreeDTOList.add(toSysDictTreeDTO(sysDictTree));
            }
        }
        return sysDictTreeDTOList;
    }

    private SysDictTreeDTO toSysDictTreeDTO(SysDictTree sysDictTree) {
        if (sysDictTree == null) {
            return null;
        }
        SysDictTreeDTO sysDictTreeDTO = new SysDictTreeDTO();
        sysDictTreeDTO.setKey(sysDictTree.getKey());
        sysDictTreeDTO.setTitle(sysDictTree.getTitle());
        sysDictTreeDTO.setId(sysDictTree.getId());
        sysDictTreeDTO.setType(sysDictTree.getType());
        sysDictTreeDTO.setDictName(sysDictTree.getDictName());
        sysDictTreeDTO.setDictCode(sysDictTree.getDictCode());
        sysDictTreeDTO.setDescription(sysDictTree.getDescription());
        sysDictTreeDTO.setDelFlag(sysDictTree.getDelFlag());
        sysDictTreeDTO.setCreateBy(sysDictTree.getCreateBy());
        sysDictTreeDTO.setCreateTime(sysDictTree.getCreateTime());
        sysDictTreeDTO.setUpdateBy(sysDictTree.getUpdateBy());
        sysDictTreeDTO.setUpdateTime(sysDictTree.getUpdateTime());
        return sysDictTreeDTO;
    }


    private List<DictModelDTO> toDictModelDTOList(List<DictModel> ls) {
        List<DictModelDTO> dictModelDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(ls)) {
            for (DictModel dictModel : ls) {
                dictModelDTOList.add(toDictModelDTO(dictModel));
            }
        }
        return dictModelDTOList;
    }

    private DictModelDTO toDictModelDTO(DictModel dictModel) {
        if (dictModel == null) {
            return null;
        }
        DictModelDTO dictModelDTO = new DictModelDTO();
        dictModelDTO.setValue(dictModel.getValue());
        dictModelDTO.setText(dictModel.getText());
        return dictModelDTO;
    }


    private List<TreeSelectModelDTO> toTreeSelectModelDTOList(List<TreeSelectModel> ls) {
        List<TreeSelectModelDTO> treeSelectModelDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(ls)) {
            for (TreeSelectModel treeSelectModel : ls) {
                treeSelectModelDTOList.add(toTreeSelectModelDTO(treeSelectModel));
            }
        }
        return treeSelectModelDTOList;
    }

    private TreeSelectModelDTO toTreeSelectModelDTO(TreeSelectModel treeSelectModel) {
        if (treeSelectModel == null) {
            return null;
        }
        TreeSelectModelDTO treeSelectModelDTO = new TreeSelectModelDTO();
        treeSelectModelDTO.setKey(treeSelectModel.getKey());
        treeSelectModelDTO.setTitle(treeSelectModel.getTitle());
        treeSelectModelDTO.setLeaf(treeSelectModel.isLeaf());
        treeSelectModelDTO.setIcon(treeSelectModel.getIcon());
        treeSelectModelDTO.setParentId(treeSelectModel.getParentId());
        treeSelectModelDTO.setValue(treeSelectModel.getValue());
        treeSelectModelDTO.setCode(treeSelectModel.getCode());
        //TODO 这里有递归逻辑，重点看下代码
        treeSelectModelDTO.setChildren(toTreeSelectModelDTOList(treeSelectModel.getChildren()));
        return treeSelectModelDTO;
    }


    private DictQuery tioDictQuery(DictQueryDTO dictQueryDTO) {
        if (dictQueryDTO == null) {
            return null;
        }
        DictQuery dictQuery = new DictQuery();
        dictQuery.setTable(dictQueryDTO.getTable());
        dictQuery.setCode(dictQueryDTO.getCode());
        dictQuery.setText(dictQueryDTO.getText());
        dictQuery.setKeyword(dictQueryDTO.getKeyword());
        dictQuery.setCodeValue(dictQueryDTO.getCodeValue());
        return dictQuery;
    }


    private List<SysDictDTO> toSysDictDTOList(List<SysDict> list) {
        List<SysDictDTO> sysDictDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (SysDict sysDict : list) {
                sysDictDTOList.add(toSysDictDTO(sysDict));
            }
        }
        return sysDictDTOList;
    }


    private List<SysDictVoDTO> toSysDictVoDTOList(List<SysDictVo> list) {
        List<SysDictVoDTO> sysDictVoDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (SysDictVo sysDictVo : list) {
                sysDictVoDTOList.add(toSysDictVoDTO(sysDictVo));
            }
        }
        return sysDictVoDTOList;
    }

    private SysDictVoDTO toSysDictVoDTO(SysDictVo sysDictVo) {
        if (sysDictVo == null) {
            return null;
        }
        SysDictVoDTO sysDictVoDTO = new SysDictVoDTO();
        sysDictVoDTO.setId(sysDictVo.getId());
        sysDictVoDTO.setDictName(sysDictVo.getDictName());
        sysDictVoDTO.setDictCode(sysDictVo.getDictCode());
        sysDictVoDTO.setLowAppId(sysDictVo.getLowAppId());
        sysDictVoDTO.setDictItemsList(toDictItemsDTOList(sysDictVo.getDictItemsList()));
        return sysDictVoDTO;
    }

    private List<SysDictItemDTO> toDictItemsDTOList(List<SysDictItem> dictItemsList) {
        List<SysDictItemDTO> sysDictItemDTOList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(dictItemsList)) {
            for (SysDictItem sysDictItem : dictItemsList) {
                sysDictItemDTOList.add(toDictItemsDTO(sysDictItem));
            }
        }
        return sysDictItemDTOList;
    }

    private SysDictItemDTO toDictItemsDTO(SysDictItem sysDictItem) {
        if (sysDictItem == null) {
            return null;
        }
        SysDictItemDTO sysDictItemDTO = new SysDictItemDTO();
        sysDictItemDTO.setId(sysDictItem.getId());
        sysDictItemDTO.setDictId(sysDictItem.getDictId());
        sysDictItemDTO.setItemText(sysDictItem.getItemText());
        sysDictItemDTO.setItemValue(sysDictItem.getItemValue());
        sysDictItemDTO.setDescription(sysDictItem.getDescription());
        sysDictItemDTO.setSortOrder(sysDictItem.getSortOrder());
        sysDictItemDTO.setStatus(sysDictItem.getStatus());
        sysDictItemDTO.setCreateBy(sysDictItem.getCreateBy());
        sysDictItemDTO.setCreateTime(sysDictItem.getCreateTime());
        sysDictItemDTO.setUpdateBy(sysDictItem.getUpdateBy());
        sysDictItemDTO.setUpdateTime(sysDictItem.getUpdateTime());
        return sysDictItemDTO;
    }


    private SysDictVo toSysDictVo(SysDictVoDTO sysDictVoDTO) {
        if (sysDictVoDTO == null) {
            return null;
        }
        SysDictVo sysDictVo = new SysDictVo();
        sysDictVo.setId(sysDictVoDTO.getId());
        sysDictVo.setDictName(sysDictVoDTO.getDictName());
        sysDictVo.setDictCode(sysDictVoDTO.getDictCode());
        sysDictVo.setLowAppId(sysDictVoDTO.getLowAppId());
        sysDictVo.setDictItemsList(toDictItemsList(sysDictVoDTO.getDictItemsList()));
        return sysDictVo;
    }

    private List<SysDictItem> toDictItemsList(List<SysDictItemDTO> dictItemsList) {
        List<SysDictItem> sysDictItemList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(dictItemsList)) {
            for (SysDictItemDTO sysDictItemDTO : dictItemsList) {
                sysDictItemList.add(toSysDictItem(sysDictItemDTO));
            }
        }
        return sysDictItemList;
    }

    private SysDictItem toSysDictItem(SysDictItemDTO sysDictItemDTO) {
        if (sysDictItemDTO == null) {
            return null;
        }
        SysDictItem sysDictItem = new SysDictItem();
        sysDictItem.setId(sysDictItemDTO.getId());
        sysDictItem.setDictId(sysDictItemDTO.getDictId());
        sysDictItem.setItemText(sysDictItemDTO.getItemText());
        sysDictItem.setItemValue(sysDictItemDTO.getItemValue());
        sysDictItem.setDescription(sysDictItemDTO.getDescription());
        sysDictItem.setSortOrder(sysDictItemDTO.getSortOrder());
        sysDictItem.setStatus(sysDictItemDTO.getStatus());
        sysDictItem.setCreateBy(sysDictItemDTO.getCreateBy());
        sysDictItem.setCreateTime(sysDictItemDTO.getCreateTime());
        sysDictItem.setUpdateBy(sysDictItemDTO.getUpdateBy());
        sysDictItem.setUpdateTime(sysDictItemDTO.getUpdateTime());
        return sysDictItem;
    }


}
